package etcdv3

import (
	"context"
	"fmt"
	"time"

	"gitee.com/zawei/yggdrasil/pkg/config/encoder"
	"gitee.com/zawei/yggdrasil/pkg/config/source"
	"go.etcd.io/etcd/api/v3/mvccpb"
	cetcd "go.etcd.io/etcd/client/v3"
)

// Currently a single etcdv3 reader
type etcd struct {
	prefix      string
	stripPrefix string
	encoder     encoder.Encoder
	client      *cetcd.Client
	cerr        error
}

func (c *etcd) Read() (*source.ChangeSet, error) {
	if c.cerr != nil {
		return nil, c.cerr
	}

	rsp, err := c.client.Get(context.Background(), c.prefix, cetcd.WithPrefix())
	if err != nil {
		return nil, err
	}

	if rsp == nil || len(rsp.Kvs) == 0 {
		return nil, fmt.Errorf("source not found: %s", c.prefix)
	}

	kvs := make([]*mvccpb.KeyValue, 0, len(rsp.Kvs))
	for _, v := range rsp.Kvs {
		kvs = append(kvs, (*mvccpb.KeyValue)(v))
	}

	data := makeMap(c.encoder, kvs, c.stripPrefix)

	b, err := c.encoder.Encode(data)
	if err != nil {
		return nil, fmt.Errorf("error reading source: %v", err)
	}

	cs := &source.ChangeSet{
		Priority:  source.PriorityRemote,
		Timestamp: time.Now(),
		Source:    c.String(),
		Data:      b,
		Format:    c.encoder.String(),
	}
	cs.Checksum = cs.Sum()

	return cs, nil
}

func (c *etcd) String() string {
	return "etcdv3"
}

func (c *etcd) Watch() (source.Watcher, error) {
	if c.cerr != nil {
		return nil, c.cerr
	}
	cs, err := c.Read()
	if err != nil {
		return nil, err
	}
	return newWatcher(c.prefix, c.stripPrefix, c.client.Watcher, cs, c.encoder)
}
